package com.gupao.edu.homework.niocommunicate;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Random;
import java.util.Set;

/**
 * @description：
 * @author：huabin.lin@xhmh.com
 * @version：2018年11月06日 21:28
 */
public class NioClientChannel {

    /***
     * 服务端的端口
     */
    private static int DEFAULT_SERVER_PORT = 8888;

    /***
     * 服务器的IP地址+端口Port
     */
    private InetSocketAddress remoteAddress;
    /***
     * 控制台输入流
     */
    private BufferedReader console;
    private String name;
    private Random rnd = new Random();

    public NioClientChannel() {
        super();
        console = new BufferedReader(new InputStreamReader(System.in));
    }

    /***
     * 发送信息
     * @throws Exception
     */
    public void start() {
        start(DEFAULT_SERVER_PORT);
    }

    /***
     * 发送信息
     * @param port
     * @throws Exception
     */
    public void start(int port) {
        this.remoteAddress = new InetSocketAddress(port);

        System.out.println("请设置您的昵称：");
        String name = this.getDataFromConsole();
        if (null == name || "".equals(name)) {
            return;
        }

        // 创建编码解码器
        Charset utf8 = Charset.forName("UTF-8");

        // 选择器
        Selector selector;
        try {
            // 创建TCP通道
            SocketChannel sc = SocketChannel.open();
            // 设置通道为非阻塞
            sc.configureBlocking(Boolean.FALSE);

            // 创建选择器
            selector = Selector.open();

            // 注册感兴趣事件
            int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;

            // 向选择器注册通道
            sc.register(selector, interestSet, new NioBuffers(256, 256));

            // 向服务器发起连接,一个通道代表一条TCP链接
            sc.connect(remoteAddress);

            // 等待三次握手完成
            while (!sc.finishConnect()) {
                ;
            }

            System.out.println(name + " " + "finished connection");
        } catch (IOException e) {
            System.out.println("client connect failed");
            return;
        }

        //<editor-fold desc="与服务器断开或线程被中断则结束线程">
        try {
            int i = 1;
            while (!Thread.currentThread().isInterrupted()) {
                // 阻塞等待
                selector.select();

                // Set中的每个key代表一个通道
                Set<SelectionKey> keySet = selector.selectedKeys();
                Iterator<SelectionKey> it = keySet.iterator();

                //<editor-fold desc="遍历每个已就绪的通道，处理这个通道已就绪的事件">
                while (it.hasNext()) {
                    SelectionKey key = it.next();
                    // 防止下次select方法返回已处理过的通道
                    it.remove();

                    // 通过SelectionKey获取对应的通道
                    NioBuffers buffers = (NioBuffers) key.attachment();
                    ByteBuffer readBuffer = buffers.getReadBuffer();
                    ByteBuffer writeBuffer = buffers.getWriteBuffer();

                    // 通过SelectionKey获取通道对应的缓冲区
                    SocketChannel sc = (SocketChannel) key.channel();

                    //<editor-fold desc="表示底层socket的读缓冲区有数据可读">
                    if (key.isReadable()) {
                        // 从socket的读缓冲区读取到程序定义的缓冲区中
                        sc.read(readBuffer);
                        readBuffer.flip();
                        // 字节到utf8解码
                        CharBuffer cb = utf8.decode(readBuffer);
                        // 显示接收到由服务器发送的信息
                        System.out.println(new StringBuffer(cb).toString());
                        readBuffer.clear();
                    }
                    //</editor-fold>

                    //<editor-fold desc="socket的写缓冲区可写">
                    if (key.isWritable()) {
                        writeBuffer.put(this.getDataFromConsole().getBytes(utf8));
                        writeBuffer.flip();
                        // 将程序定义的缓冲区中的内容写入到socket的写缓冲区中
                        sc.write(writeBuffer);
                        writeBuffer.clear();
                        i++;
                    }
                    //</editor-fold>
                }
                //</editor-fold>

                Thread.sleep(1000 + rnd.nextInt(1000));
            }
        } catch (InterruptedException e) {
            System.out.println(name + " is interrupted");
        } catch (IOException e) {
            System.out.println(name + " encounter a connect error");
        } finally {
            try {
                selector.close();
            } catch (IOException e1) {
                System.out.println(name + " close selector failed");
            } finally {
                System.out.println(name + " closed");
            }
        }
        //</editor-fold>
    }

    /***
     * 从控制台获取数据
     * @return
     */
    private String getDataFromConsole() {
        try {
            if (null == this.name || "".equals(this.name)) {
                this.name = this.console.readLine();
                return this.name;
            }
            return this.name + "#@#" + this.console.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }
}
